home *** CD-ROM | disk | FTP | other *** search
/ Night Owl 6 / Night Owl's Shareware - PDSI-006 - Night Owl Corp (1990).iso / 029a / wc526.zip / WC526.ASM < prev   
Assembly Source File  |  1991-10-10  |  28KB  |  1,093 lines

  1. TITLE  Word Count, WC V5.26. Program to count lines, words and chars.
  2.  
  3. IFDEF ZTIMER
  4. extrn   ZTimerOn:near, ZTimerOff:near, ZTimerReport:near
  5. ENDIF
  6.  
  7.         LOCALS                          ; Use TASM local labels
  8.  
  9. dosint      MACRO  function            ;; Call the DOS interrupt
  10.     mov    ah,function         ;; Put function number in AH
  11.     int    21h
  12.     ENDM
  13.  
  14. code segment page public use16 'CODE'   ; Put code segment first,
  15. code ends
  16.  
  17. data segment word public use16 'DATA'   ; but declare the data values first
  18.                                         ; to avoid forward references!
  19. SyntaxMsg label byte
  20.  
  21.  db "Word Count V5.26 (C) Terje Mathisen 1986, 1990, 1991 (486-optimized)",13,10
  22.  db 13,10
  23.  db ' Count lines, words and characters in text files.',13,10
  24.  db ' Syntax: WC [/option] PathSpec [PathSpec .. ]',13,10,10
  25.  db ' Options:',13,10
  26.  db '  /l       : Count LINEs',13,10
  27.  db '  /w       : Count WORDs',13,10
  28.  db '  /c       : Count CHARs',13,10
  29.  db '  /r       : Use CR, LF or CRLF as line terminator',13,10
  30.  db "  /z       : Use Ctrl-Z as EOF mark.",13,10
  31.  db '  /t       : Only show the Total counts for multiple files.',13,10
  32.  db '  /s<list> : Use the chars in <list> as word separators.',13,10
  33.  db ' If neither /w, /l or /c is given, all will be counted.',13,10
  34.  db '  TAB, LF, CR and SPACE are always treated as separators.',13,10
  35.  db ' If no separator list is given, ",.!#%&=?^;:" will be used.',13,10
  36.  db '$'
  37.  
  38. NoFilesMsg      db  "No files found"
  39. CrLf            db  13,10,"$"
  40.  
  41. OpenErrMsg   db  "Open error",0dh,0ah,"$"
  42. CloseErrMsg  db  "Close error",0dh,0ah,"$"
  43. ReadErrMsg   db  "Read error",0dh,0ah,"$"
  44. RamErrMsg    db  'Not enough RAM',13,10,'$'
  45.  
  46. total$          db  "  total in $"
  47. files$          db  ' files',13,10,'$'
  48.  
  49. UseCrLf         db 0                    ; 0 -> Use only LF to count new lines
  50. MakeJumpTable   db 1                    ; 1 -> Make Jump table before use
  51. ShowTotals      db 0                    ; 0 -> Show all files
  52. CpuType         db ?                    ; Save Cpu type (2=286,3=386 etc)
  53.  
  54.         EVEN
  55.  
  56. c_flags db      0                       ; Flags for types of count
  57. or_flag db      0FFh                    ; Assume to count everything
  58.  
  59. EofChar db      26                      ; Assume Ctrl-Z = EOF
  60. SkipEof db      1                       ; 0 to use Ctrl-Z as EOF
  61.  
  62. ; Total count of lines, words and chars
  63.  
  64. l_cnt_t dw      0,0
  65. w_cnt_t dw      0,0
  66. c_cnt_t dw      0,0
  67. f_cnt   dw      0
  68.  
  69. WhiteLen        dw offset WhiteEnd - Offset WhiteSpace
  70.  
  71. WhiteSpace    db 9,10,13,' '            ; Use these ctrl chars as whitespace
  72.  
  73. EndProg label byte                      ; The EDATA structure starts here
  74.  
  75.         db ',.!#%&=?^;:'                ; Default word separators
  76.  
  77. WhiteEnd label byte
  78.  
  79. data ends
  80.  
  81. stack_seg segment public word use16 'STACK' ; To help the linker!
  82. stack_seg ends
  83.  
  84. dgroup group code, data, stack_seg      ; Everything in one segment for .COM
  85.  
  86.         assume cs:dgroup,ds:dgroup,es:dgroup,ss:dgroup
  87.  
  88.  
  89. edata struc           ; Un-Initialized vars @ endprog (WORD alignment!)
  90.  
  91. WhiteTable  db 120 dup (?)              ; Overlayed on EndProg
  92.  
  93. spaces      db  2 dup (?)               ; Two leading spaces for filename
  94.  
  95. filename    db 80 dup (?)
  96. name_start  dw  1 dup (?)               ; Start of name part of pathname
  97.  
  98. BufferSize  dw  1 dup (?)               ; Size of file buffer
  99.  
  100. BufSeg      dw  1 dup (?)               ; Segment for buffer
  101. JumpSeg     dw  1 dup (?)               ; Segment for 64 K lookup table
  102.  
  103. l_cnt       dw  2 dup (?)
  104. w_cnt       dw  2 dup (?)
  105. c_cnt       dw  2 dup (?)
  106.  
  107. FileBuffer  dw  1 dup (?)
  108.  
  109. dta     db 21 dup (?)
  110. dtaattr db ?
  111. dtatime dd ?
  112. dtasize dd ?
  113. dtaname db 13 dup (?)
  114.  
  115. Last_Char       db ?                    ; Save the last char in the buffer
  116.  
  117. FileBufferStart db 1 dup (?)            ; Dummy, will be overlayed
  118.  
  119. edata ends
  120.  
  121. BufSize         equ 0F000h              ; Try to get 60 kB file buffer
  122.  
  123. BufferData struc        ; Layout of 2nd lookup table
  124.  
  125.                 db 256 dup (?)          ; First part of lookup
  126.   Xlate_Tab     db 256 dup (?)          ; char-to-type table (temporary)
  127.                 db 512 * 15 dup (?)     ; Rest of lookup table
  128.  
  129. BufferData ends
  130.  
  131. l_bit   equ 1
  132. w_bit   equ 2
  133. c_bit   equ 4
  134.  
  135. StackSize  equ 200h                     ; Use 512B for stack
  136.  
  137. code    SEGMENT
  138.     ORG 100h
  139.  
  140. main    proc    far
  141. start:
  142.     cld
  143.  
  144.         mov     bx, 1000h               ; We want 64 K initially
  145.         mov     ah, 4Ah
  146.         int     21h                     ; DOS SetBlock (Want 4kB)
  147.          jc     @@RamError
  148.         
  149.         mov     cx, BufSize
  150.         mov     [DGROUP:endprog.BufferSize], cx
  151.  
  152.         mov     bx, ((type BufferData) - 1) SHR 4 + 1 ; == 200h
  153.         mov     ah, 48h
  154.         int     21h                     ; Allocate RAM for 2nd lookup table:
  155.          jc     @@RamError
  156.  
  157.         mov     [DGROUP:endprog.BufSeg], ax
  158.         
  159.         mov     bx, 1000h               ; We want 64 k for state machine
  160.         mov     ah, 48h
  161.         int     21h                     ; Allocate RAM for jump table & buffer
  162.          jnc    @@RamOK
  163.         
  164. @@RamError: 
  165.         mov     dx, offset DGROUP:RamErrMsg
  166.         jmp     ErrorExit
  167.  
  168. @@RamOK:
  169.         mov     [DGROUP:endprog.JumpSeg], ax
  170.  
  171.         lea     ax,[DGROUP:endprog.FileBufferStart]
  172.         add     ax,15
  173.         and     ax,NOT 15
  174.         mov     [DGROUP:endprog.FileBuffer],ax
  175.  
  176.         call    GetCpuType
  177.  
  178.         call    InitTable2              ; Build 8K increment table
  179.  
  180. ; Move default DTA away from the command line which we are parsing
  181.  
  182.         mov     dx, offset DGROUP:endprog.dta
  183.         mov     ah, 1Ah
  184.         int     21h                     ; SET DTA!
  185.  
  186. ; Start parsing any commands given us
  187.  
  188.     mov    si,81h            ; Start of parameter area
  189.  
  190.         mov     word ptr [DGROUP:endprog.spaces],2020h  ; Init to 2 spaces
  191.  
  192. ; Here is where the main parsing loop starts.
  193.  
  194. SkipWhite:
  195.     lodsb
  196.  
  197.     CMP    al,13
  198.      JNE    @@NotCR
  199.  
  200. ; We have got the terminating CR. Check if NO filname has been given:
  201.  
  202.         cmp     [DGROUP:f_cnt],0
  203.          ja     @@NearTotal
  204.  
  205. ; No filename given, so run as a filter for STDIN
  206.  
  207.     mov    bx,0                    ; stdin handle
  208.     call    do_file                 ; Count chars, words etc.
  209.     mov    dx,offset DGROUP:crlf   ; WriteLn
  210.     dosint  9
  211.  
  212. @@NearTotal:
  213.      jmp    @@TestTotal             ; Goto end
  214.  
  215. @@NotCR:
  216.     CMP    al,' '
  217.      JBE    SkipWhite
  218.  
  219. @@HaveParam:
  220.         cmp     al,'-'                  ; Allow '-' & '/' to start options
  221.          je     @@Option
  222.         cmp     al,'/'
  223.          jne    @@NotOption
  224.  
  225. @@Option:
  226.         jmp     ParseOption
  227.  
  228.  
  229. @@NotOption:                            ; Not an option, so it must be a file
  230.  
  231.     mov    di,offset DGROUP:endprog.filename
  232.     mov    [DGROUP:endprog.name_start],di
  233.     mov    dx,di            ; Save for Find first
  234.  
  235. @@MoveName:
  236.     stosb                           ; Save in <filename> buffer
  237.  
  238.     cmp al,'\'                      ; Look for the end of the path
  239.      je @@path_end
  240.     cmp al,':'
  241.      jne @@next_char
  242.  
  243. @@path_end:
  244.     mov    [DGROUP:endprog.name_start],di  ; The name part starts here
  245.  
  246. @@next_char:
  247.     lodsb
  248.     CMP    al,' '
  249.      JA    @@MoveName
  250.  
  251.     mov    byte ptr [di],0        ; Make it ASCIIZ
  252.  
  253.     xor cx,cx                          ;Only normal files
  254.     dosint 4eh                         ;Find first
  255.         mov     dx, offset DGROUP:NoFilesMsg
  256.      JC    NearError
  257.  
  258.         push    si
  259.  
  260. @@FileLoop:
  261.     mov    si,offset DGROUP:endprog.dtaname ; Copy the filename
  262.     mov    di,[DGROUP:endprog.name_start]     ;  in after the path
  263.  
  264. @@MoveTail:
  265.     lodsb                           ; Add the result from FINDFIRST to the
  266.     stosb                           ; path spec from the parameter
  267.     OR    al,al
  268.      JNE    @@MoveTail
  269.  
  270.     mov    dx,offset DGROUP:endprog.filename ; Filename
  271.     mov    ax,3D00h        ; Open MODE = read
  272.     int     21h
  273.  
  274.         mov     dx, offset DGROUP:OpenErrMsg
  275.      JC     NearError
  276.  
  277.     mov    bx,ax            ; Handle of file
  278.  
  279.     call do_file
  280.  
  281.     dosint 3eh                     ; Close file
  282.         mov     dx, offset DGROUP:CloseErrMsg
  283.      JC     NearError
  284.  
  285.         cmp     [DGROUP:ShowTotals],1
  286.          JE     @@SkipDisplay
  287.  
  288.     mov    di,offset DGROUP:endprog.spaces ; Offset of filename w/ spaces
  289.     call printz                    ;  print it!
  290.  
  291.     mov    dx, offset DGROUP:crlf        ; print CR,LF
  292.     dosint 9
  293.  
  294. @@SkipDisplay:
  295.     dosint 4fh                     ;Find next match
  296.      JNC    @@FileLoop
  297.  
  298.         pop     si                      ; Restore SI (input parser)
  299.         dec     si                      ; Re-Read last char (maybe CR?)
  300.  
  301.         jmp     SkipWhite               ; Restart parsing loop
  302.  
  303. @@TestTotal:
  304.  
  305.         CMP     [DGROUP:ShowTotals],1   ; Have the user asked (/t) for totals?
  306.          JE     @@ShowTotal
  307.  
  308.     CMP    [DGROUP:f_cnt],1        ; Have we got just one file?
  309.      JE    @@OneFile               ;  Yes, so no total display
  310.  
  311. @@ShowTotal:
  312.         mov     dx, offset DGROUP:NoFilesMsg
  313.          JB     NearError               ;  No files found at all!
  314.  
  315.         mov     si, offset DGROUP: l_cnt_t
  316.         call    PrintNum
  317.  
  318.     mov    dx,offset DGROUP:total$
  319.     dosint 9
  320.  
  321.         mov     ax, [DGROUP:f_cnt]
  322.         xor     dx, dx
  323.         mov     di, 1
  324.         call    prtascii
  325.  
  326.         mov     dx, offset DGROUP:files$
  327.         dosint  9
  328.  
  329. @@OneFile:
  330.  
  331. finish:
  332.  
  333.     mov     ax,4C00h             ; Set 0 for return code
  334.         int     21h
  335.  
  336. main    endp
  337.  
  338. NearError:
  339.         jmp     ErrorExit
  340.  
  341. printzero  PROC    NEAR               ; Procedure to print ASCIIZ lowercase
  342.  
  343. @@p1:
  344.     CMP    dl,'A'
  345.      JB    @@NotUpper
  346.     CMP    dl,'Z'
  347.      JA    @@NotUpper
  348.     add     dl,'a'-'A' ; Make it lowercase
  349.  
  350. @@NotUpper:
  351.     dosint  2
  352.     inc     di
  353.  
  354. printz:                                 ; PROC Entry HERE!
  355.     mov    dl,byte ptr [di]
  356.     OR    dl,dl
  357.          jnz    @@p1
  358.  
  359.     ret
  360. printzero  ENDP
  361.  
  362. ReadOption  proc near
  363.  
  364.         cmp     byte ptr [si],' '
  365.          ja     ParseOption             ; Yes, read another option char
  366.  
  367.         jmp     SkipWhite
  368.  
  369. ParseOption:                            ; Start here, we have a '/' or '-'
  370.         lodsb
  371.         cmp     al,'a'
  372.          jb     @@NotLower
  373.         cmp     al,'z'
  374.          ja     @@NotLower
  375.  
  376.         sub     al,'a'-'A'
  377.  
  378. @@NotLower:
  379.  
  380. ; Is this C, W or L ?
  381.  
  382.         cmp     al, 'C'
  383.         mov     bx, c_bit
  384.          je     @@SetOption
  385.  
  386.         cmp     al, 'W'
  387.         mov     bl, w_bit
  388.          je     @@SetOption
  389.  
  390.         cmp     al, 'L'
  391.          jne     @@TestSep
  392.         mov     bl, l_bit
  393.  
  394. @@SetOption:
  395.         or      [DGROUP:c_flags],bl
  396.         mov     [DGROUP:or_flag],bh     ; Set to 0 (No default)
  397.  
  398.         jmp     ReadOption
  399.  
  400. ; It might be a list of word separators:
  401.  
  402. @@TestSep:
  403.         cmp     al, 'S'                 ; Separator list
  404.          je     @@GetSep
  405.  
  406.         cmp     al,'T'                  ; total display?
  407.          jne    @@TestEof
  408.         mov     [DGROUP:ShowTotals],1
  409.         jmp     ReadOption
  410.  
  411. @@TestEOF:
  412.         cmp     al, 'Z'                 ; Stop on Ctrl-Z in files (EOF mark)?
  413.          jnz    @@TestCrLf
  414.         mov     [DGROUP:SkipEof], 0
  415.  
  416.         jmp     ReadOption
  417.  
  418. @@TestCrLf:
  419.         cmp     al, 'R'                 ; Allow CR as line terminator
  420.         mov     dx, offset DGROUP:SyntaxMsg
  421.          jnz    ErrorExit
  422.  
  423.         mov     [DGROUP:UseCrLf],1
  424.         mov     [DGROUP:MakeJumpTable],1
  425.  
  426.         jmp     ReadOption
  427.  
  428. @@GetSep:
  429.         mov     di, offset DGROUP:EndProg.WhiteTable
  430. @@G1:
  431.         lodsb
  432.         stosb
  433.         cmp     al,' '
  434.          ja     @@G1
  435.  
  436.         dec     di
  437.         sub     di, offset DGROUP:WhiteSpace
  438.         mov     [DGROUP:WhiteLen],di
  439.  
  440.         mov     [DGROUP:MakeJumpTable],1
  441.  
  442.         dec     si                      ; Reload last char
  443.         jmp     SkipWhite
  444.  
  445. ReadOption endp
  446.  
  447. ErrorExit proc near
  448.  
  449.         push    cs
  450.         pop     ds                      ; In case DS was destroyed
  451.         ASSUME DS:DGROUP
  452.  
  453.         mov     ah,9
  454.         int     21h
  455.  
  456.         mov     ax,4C01h
  457.         int     21h
  458.  
  459. ErrorExit endp
  460.  
  461. do_file proc    near
  462. ;   bx = handle
  463. ;   Regs:
  464.  
  465.     push    bx
  466.     push    cx
  467.     push    dx
  468.         push    si
  469.     push    di
  470.     push    bp
  471.  
  472. ; Init local vars (lines, words and chars)
  473.  
  474.     mov    di,offset DGROUP:endprog.l_cnt
  475.     mov    cx,6
  476.     xor     ax,ax
  477.     rep stosw
  478.  
  479.         cmp     [DGROUP:MakeJumpTable], al
  480.          jz     @@1                     ; Table already OK
  481.  
  482.         call    InitJumpTable           ; Initialize table before start
  483.  
  484. @@1:
  485.         inc     [DGROUP:f_cnt]          ; One more file!
  486.  
  487.         mov     es,[DGROUP:endprog.BufSeg]
  488.         mov     ds,[DGROUP:endprog.JumpSeg]
  489.         ASSUME DS:NOTHING, ES:NOTHING
  490.  
  491.         mov     [DGROUP:endprog.Last_Char], 10  ; Assume a LF in front of file
  492.  
  493. @@ReadBuffer:
  494.     mov    cx, [DGROUP:endprog.BufferSize]
  495.         mov     dx, [DGROUP:endprog.FileBuffer]
  496.         mov     bp, dx
  497.  
  498.         push    ds
  499.         push    cs
  500.         pop     ds
  501.     dosint  3fh                    ;Read a block
  502.         pop     ds
  503.  
  504.         mov     dx, offset ReadErrMsg
  505.      JC    ErrorExit
  506.  
  507.     mov    cx,ax            ; Buffer count
  508.      JCXZ    @@EndOfFile
  509.  
  510.         cmp     [DGROUP:SkipEof],0
  511.          jne    @@SkipEof
  512.  
  513.         push    cx es
  514.  
  515.         push    cs
  516.         pop     es
  517.         mov     di,si
  518.         mov     al,1Ah
  519.   repne scasb
  520.         pop     es cx
  521.         
  522.          jne    @@SkipEof
  523.  
  524.         dec     di                      ; Point to the Ctrl-Z
  525.         sub     di,si                   ; Calculate length before the EOF
  526.          jbe    @@EndOfFile
  527.  
  528.         mov     cx,di
  529.         sub     al,al                   ; Set Zero flag
  530.  
  531. @@SkipEof:
  532.         pushf
  533.         call    count                   ; Count this block (SS:BP)
  534.         popf
  535.  
  536.      jne    @@ReadBuffer            ; Try another!
  537.  
  538. @@EndOfFile:
  539.         mov     al,[DGROUP:endprog.Last_Char]       ; check if LF was last char
  540.  
  541.         push    cs
  542.         pop     ds
  543.         ASSUME DS:DGROUP
  544.         push    cs
  545.         pop     es
  546.         ASSUME ES:DGROUP
  547.  
  548. ; If the last line did not end in LF, we must add one to the line-count:
  549.  
  550.         mov     si, offset DGROUP:endprog.l_cnt
  551.         cmp     al,10
  552.          je     @@Normal
  553.  
  554.         cmp     [DGROUP:UseCrLf],0
  555.          je     @@IncLines
  556.  
  557.         cmp     al,13
  558.          je     @@Normal
  559.  
  560. @@IncLines:
  561.         add     word ptr [si],1
  562.         adc     word ptr [si+2],0
  563.  
  564. @@Normal:
  565.         cmp     [DGROUP:ShowTotals],1
  566.          JE     @@SkipPrint
  567.  
  568.         push    si
  569.     call    PrintNum
  570.         pop     si
  571.  
  572. @@SkipPrint:
  573.  
  574.         mov     cx, 3
  575.         mov     di, offset DGROUP:l_cnt_t
  576.  
  577. @@AddLoop:                              ; Add counts for this file to total
  578.         lodsw
  579.         add     [di],ax
  580.         lodsw
  581.         adc     [di+2],ax
  582.  
  583.         add     di,4                    ; Point to next value
  584.  
  585.          loop   @@AddLoop
  586.  
  587.     pop     bp
  588.     pop     di
  589.         pop     si
  590.     pop     dx
  591.     pop     cx
  592.     pop     bx
  593.  
  594.     ret
  595. do_file endp
  596.  
  597. PrintNum proc near
  598. ; SI -> lines, words and chars
  599.         
  600.         push    bx
  601.         push    cx
  602.         push    dx
  603.         push    di
  604.  
  605.         mov     bx, word ptr [DGROUP:c_flags]
  606.         or      bl, bh                  ; OR in default bits
  607.         mov     cx, 3
  608.  
  609.         mov     di, 8                   ; Min length for lines (+ 1 space)
  610.  
  611. @@PrtLoop:
  612.         lodsw
  613.         mov     dx,ax
  614.         lodsw
  615.  
  616.         shr     bx,1
  617.          jnc    @@Skip
  618.  
  619.         xchg    dx,ax
  620.  
  621.         call    prtascii
  622.  
  623. @@Skip:
  624.         inc     di                      ; Increment room for each type
  625.          loop   @@PrtLoop
  626.  
  627.         pop di
  628.         pop dx
  629.         pop cx
  630.         pop ax
  631.  
  632.         ret
  633. PrintNum endp
  634.  
  635. prtascii   proc c near uses bx cx si di,
  636. ;       Input
  637. ;           dx:ax   : 32 bit unsigned number (Destroyed)
  638. ;           di      : min length (Not modified)
  639.  
  640. MAXBUFLEN = 32
  641.  
  642. local   len:WORD, buffer:BYTE:MAXBUFLEN
  643.  
  644.         mov     [len],di
  645.         mov     cx,MAXBUFLEN SHR 1
  646.         lea     di,[buffer]
  647.         mov     si,ax
  648.         mov     ax,' '+ ' ' * 256
  649.     rep stosw
  650.         mov     ax,si
  651.         mov     si,di                   ; ptr to end of buffer
  652.  
  653.         push    bp
  654.  
  655.     mov     bp,dx                   ;save high word
  656.     mov    bx,10                 ; You have ten fingers
  657.  
  658.         mov     cx,3                    ; Comma distance
  659.         or      bp,bp                   ; upper 16 bits zero?
  660.          jz     @@next16
  661.  
  662. @@next32:
  663.     xor     dx,dx                   ;zero dx for divide
  664.     xchg    ax,bp                   ;get high word
  665.     div     bx
  666.     xchg    bp,ax                   ;return low word
  667.     div     bx
  668.  
  669.         add     dl,'0'
  670.         dec     di
  671.         mov     [di],dl                 ; Save char
  672.  
  673. @@Test32:
  674.         or      bp, bp                  ; 32 bit done (BP = 0 )?
  675.          jz     @@Test16
  676.  
  677.         loop    @@next32
  678.  
  679.         mov     dl,','
  680.         dec     di
  681.         mov     [di],dl                 ; Save char
  682.  
  683.         mov     cl,3
  684.          jmp    @@next32
  685.  
  686. @@next16:
  687.     xor     dx,dx                   ;zero dx for divide
  688.     div     bx
  689.  
  690.         add     dl,'0'
  691.         dec     di
  692.         mov     [di],dl                 ; Save char
  693.  
  694. @@Test16:
  695.         or      ax, ax                  ; 32 bit done (BP = 0 )?
  696.          jz     @@Spaces
  697.  
  698.         loop    @@next16
  699.  
  700.         mov     dl,','
  701.         dec     di
  702.         mov     [di],dl                 ; Save char
  703.  
  704.         mov     cl,3
  705.          jmp    @@next16
  706.  
  707. @@Spaces:
  708.         dec     di                      ; Point to leading space
  709.         mov     dx,di                   ; ptr for dos write
  710.  
  711.         pop     bp                      ; Restore wanted length
  712.  
  713.         mov     cx,si
  714.         sub     cx,dx                   ; CX = Current length
  715.         mov     si,[len]                ; Min length
  716.         sub     si,cx
  717.          jbe    @@LenOK                 ; Yepp, it's long enough!
  718.  
  719.         sub     dx,si                   ; SI > 0
  720.         add     cx,si
  721. @@LenOK:
  722.         mov     ah,40h
  723.         mov     bx,1                    ;STDOUT handle
  724.         int     21h                     ; Print buffer on stack
  725.  
  726.     ret
  727. prtascii   endp
  728.  
  729. GetCpuType      proc near
  730.  
  731.         pushf                           ; Save flags, will get trashed!
  732.  
  733.         xor     cx,cx                   ; Assume 808x
  734.  
  735.         xor     ax,ax
  736.         push    ax
  737.         popf                            ; Try to zero all flags!
  738.         pushf
  739.         pop     ax                      ; Retrieve them!
  740.         and     ah,0F0h
  741.         cmp     ah,0F0h                 ; Are all 4 top bits set?
  742.          jz     @@GotCpu                ; Yes, so we're done (808x!)
  743.  
  744.         inc     cx                      ; Must be 80286 or later
  745.         inc     cx
  746.  
  747.         mov     ax,0F000h
  748.         push    ax
  749.         popf
  750.         pushf
  751.         pop     ax                      ; Try to set top 4 bits
  752.  
  753.         and     ah,0F0h
  754.          jz     @@GotCPU                ; Must be 286, no bits are set
  755.  
  756.         inc     cx                      ; Bravo! This is at least a 386
  757.         mov     [word ptr JumpCode],850Fh ; JNZ near
  758.         mov     [word ptr JumpCode+2],offset LoopTop - (offset JumpCode + 4)
  759.         mov     [byte ptr JumpCode+4],90h ; NOP
  760.  
  761. @@GotCPU:                               ; We have 286 or 386, update name
  762.         mov     [CpuType],cl
  763.  
  764.         popf                            ; Restore orig. flags
  765.         ret
  766.  
  767. GetCpuType      endp
  768.  
  769. InitTable2      proc near
  770.         push    ax bx cx si di ds es
  771.  
  772.         mov     es,[DGROUP:endprog.BufSeg]
  773.         ASSUME  ES:NOTHING
  774.  
  775.         cld
  776.         xor     di, di
  777.         mov     bh, 4
  778. @@Loop1:
  779.         mov     si, offset @@Table2
  780.         mov     bl, 4
  781. @@Loop2:
  782.         mov     cx, 16
  783.     rep movsw
  784.         add     di, 512 - 32
  785.  
  786.         dec     bl
  787.          jnz    @@Loop2
  788.  
  789.         dec     bh
  790.          jnz    @@Loop1
  791.  
  792.         mov     ds,[DGROUP:endprog.BufSeg]
  793.         ASSUME  DS:NOTHING
  794.  
  795.         mov     ch,30
  796. @@Loop3:
  797.         mov     cl,30
  798. @@Loop4:
  799.         mov     bx,cx
  800.         mov     ax,[bx]
  801.         xchg    bl,bh
  802.         mov     [bx+32],ax
  803.         sub     cl,2
  804.          jae    @@Loop4
  805.  
  806.         sub     ch,2
  807.          jae    @@Loop3
  808.  
  809.         pop     es ds di si cx bx ax
  810.         ASSUME  DS:DGROUP
  811.         ret
  812.  
  813. @@Table2:
  814.  
  815. ;   WW WS  WC   WL   SW  SS  SC   SL   CW   CS   CC   CL   LW   LS   LC   LL
  816. ; First table is when last was xW
  817.  dw 0h,0h,100h,100h,001h,0h,100h,100h,101h,100h,200h,100h,101h,100h,200h,200h
  818.  
  819. ;xS
  820.  dw 1h,1h,101h,101h,001h,0h,100h,100h,101h,100h,200h,100h,101h,100h,200h,200h
  821.  
  822. ;xC
  823.  dw 1h,1h,101h,101h,001h,0h,100h,100h,101h,100h,200h,100h,001h,000h,100h,100h
  824.  
  825. ;xL = xL
  826.  dw 1h,1h,101h,101h,001h,0h,100h,100h,101h,100h,200h,100h,101h,100h,200h,200h
  827.  
  828. InitTable2      endp
  829.  
  830. InitJumpTable proc near
  831.  
  832.         push    bx cx dx si di bp ds es
  833.  
  834.         mov     es, [DGROUP:endprog.BufSeg]
  835.         ASSUME ES:NOTHING
  836.  
  837. ; First: Fill the table with WORD code == 0
  838.  
  839.         mov     di, 256
  840.         mov     cx, 128
  841.         xor     ax, ax
  842.     rep stosw
  843.  
  844.         mov     si, offset DGROUP:WhiteSpace
  845.         mov     cx, [DGROUP: WhiteLen]
  846.         mov     al, 2 * 4               ; Separator index
  847.         mov     bh, 1
  848.  
  849. @@GetWhite:
  850.         mov     bl, [DGROUP:si]         ; Get one char in DI
  851.         inc     si                      ; Point to next
  852.  
  853.         mov     [es:bx],al              ; Write flag value
  854.  
  855.          loop   @@GetWhite
  856.  
  857. ; Set CR code to 4 ?
  858.         cmp     [DGROUP:UseCrLf],0      ; Treat CRLF special?
  859.          jz     @@SkipCR
  860.  
  861.         mov     byte ptr [es:256+13],4 * 4
  862.  
  863. @@SkipCR:
  864. ; Set LineFeed code to 6:
  865.  
  866.         mov     byte ptr [es:256+10],6 * 4
  867.  
  868.         mov     es, [DGROUP:endprog.JumpSeg]
  869.         mov     ds, [DGROUP:endprog.BufSeg]
  870.         ASSUME ES:NOTHING, DS:NOTHING
  871.  
  872.         xor     di,di                   ; Start of 64K output segment
  873.         xor     bx,bx                   ; High byte counter
  874.         cmp     [CpuType],3
  875.          jae    @@MakeTable386
  876.  
  877.         ALIGN   2
  878.         UNROLL = 4
  879. @@Make1:
  880.         mov     si,256                  ; offset to 256-byte lookup table
  881.         mov     dl,[bx+si]              ; High byte of pair to look up
  882.         shr     dl,1
  883.         shr     dl,1                    ; move result to lower 2 bits
  884.         mov     dh,dl                   ; duplicate in DH so we can do words
  885.  
  886.         mov     cx,256 / (UNROLL * 2)   ; 32 * 4 WORDs == 256 bytes
  887.  
  888.         ALIGN   2
  889. @@Make2:
  890.         REPT    UNROLL
  891.         lodsw                           ; Get next 2 chars from lookup table
  892.         or      ax,dx                   ; Insert lower 2 bits in AL and AH
  893.         stosw                           ; Store in output table
  894.         ENDM
  895.  
  896.          loop   @@Make2
  897.  
  898.         inc     bl
  899.          jnz    @@Make1
  900.          jmp    @@done
  901.  
  902. @@MakeTable386:
  903.         UNROLL = 4
  904.  
  905.         mov     bp, UNROLL * 4
  906.  
  907.         ALIGN   4
  908. @@Make3:
  909.         .386
  910.         mov     si,256                  ; offset to 256-byte lookup table
  911.         mov     dl,[bx+si]              ; High byte of pair to look up
  912.         shr     dl,1
  913.         shr     dl,1                    ; move result to lower 2 bits
  914.         mov     dh,dl                   ; duplicate in DH so we can do words
  915.         mov     ax,dx
  916.         shl     edx,16
  917.         mov     dx,ax                   ; EDX has 4 copies of lower 2 bits
  918.  
  919.         mov     cx,256 / (UNROLL * 4)   ; 16 * 4 DWORDs == 256 bytes
  920.  
  921.         ALIGN   16
  922. @@Make4:
  923.         OFFS = 0
  924.         REPT UNROLL
  925.  
  926.         mov     eax,[si+OFFS]           ;; Get next 4 chars from lookup table
  927.         or      eax,edx                 ;; Insert lower 2 bits in all 4 bytes
  928.         mov     [es:di+OFFS],eax        ;; Store in output table
  929.  
  930.         OFFS = OFFS + 4
  931.         ENDM
  932.  
  933.         .8086
  934.  
  935.         add     si,bp
  936.         add     di,bp
  937.  
  938.          loop   @@Make4
  939.  
  940.         inc     bl
  941.          jnz    @@Make3
  942.  
  943. @@done:
  944.         pop     es ds bp di si dx cx bx
  945.         ASSUME  DS:DGROUP, ES:DGROUP
  946.  
  947.         mov     [DGROUP:MakeJumpTable], 0 ; Table is OK!
  948.  
  949.         ret
  950.  
  951. InitJumpTable endp
  952.  
  953. ;----------------------------------------------------------------------------
  954. ;
  955. ; This is the counting function. It is optimized for 486 CPU's.
  956. ;
  957. ;----------------------------------------------------------------------------
  958.  
  959. count   proc    near
  960. ;   ss:bp = ptr to data buffer
  961. ;   ds    = segment of double-byte lookup table
  962. ;   es    = segment of index-to-increments table
  963. ;   cx    = # of char's
  964.  
  965.         ASSUME  DS:NOTHING, ES:NOTHING
  966.  
  967.     push    bx                      ; File handle, save!
  968.         push    dx
  969.         push    di
  970.  
  971.     add     [DGROUP:endprog.c_cnt],cx ; Add # of bytes
  972.     adc     [DGROUP:endprog.c_cnt+2],0
  973.  
  974.         mov     di,cx
  975.         mov     al,[bp+di-1]            ; Last char in buffer
  976.         mov     bh,al
  977.         xchg    bh,[DGROUP:endprog.Last_Char]       ; Save it and retrieve the last
  978.  
  979.         or      cl,cl
  980.          jz     @@even_page
  981.  
  982. ; Fill out the last 256-byte block with duplicates of the last char,
  983. ; unless it's a CR or LF, where I use space instead
  984.  
  985.         add     di,bp
  986.  
  987.         cmp     al,13
  988.          je     @@IsCr
  989.         cmp     al,10
  990.          jne    @@NotLF
  991. @@IsCr:
  992.         mov     al,' '
  993. @@NotLF:
  994.         mov     ah,al                   ; Duplicate for STOSW
  995.  
  996.         push    es
  997.  
  998.         push    ss
  999.         pop     es
  1000.  
  1001.         mov     bl,ch
  1002.         neg     cx
  1003.         xor     ch,ch
  1004.         shr     cx,1
  1005.          jnc    @@even
  1006.         stosb
  1007. @@even:
  1008.     rep stosw
  1009.  
  1010.         inc     bx
  1011.         mov     ch,bl
  1012.  
  1013.         pop     es
  1014.  
  1015. @@even_page:                            ; CH = # of 256-byte blocks
  1016.  
  1017. IFDEF   ZTIMER
  1018.         call    ZTimerOn
  1019. ENDIF
  1020.  
  1021.         xchg    ch,cl
  1022.         mov     si,cx                   ; Loop counter
  1023.  
  1024.         mov     bh,[DS:bx]              ; Lookup last valid state
  1025.  
  1026.         mov     di,[bp]                 ; Get first pair of chars
  1027.  
  1028.         add     bp,130                  ; Point into the middle of the block
  1029.  
  1030. OFFS = -130
  1031.  
  1032.         xor     cx,cx                   ; # of words, 16 bit
  1033.     xor     dx,dx                   ; # lines in DH and words in DL
  1034.         xor     ax,ax                   ; # of lines, 16 bit
  1035.  
  1036.         ALIGN   16
  1037. LoopTop:
  1038.         REPT    64
  1039.         OFFS    = OFFS + 2
  1040.                 mov     bl,[di]         ; Lookup new BL value
  1041.                 mov     di,[bp+OFFS]    ; Get 2 chars
  1042.                 add     dx,[es:bx]      ; BX -> count for words & lines
  1043.  
  1044.         OFFS    = OFFS + 2
  1045.                 mov     bh,[di]         ; Lookup state value (second byte)
  1046.                 mov     di,[bp+OFFS]    ; Get next 2 chars
  1047.                 add     dx,[es:bx+32]   ; BX+32 -> second table
  1048.         ENDM
  1049.  
  1050. ; If the previous 256-byte block was 256 LF's, the line count (in DH) will
  1051. ; have overflowed. I fix that by using an ADC AH,0 to add in the carry.
  1052.  
  1053.         adc     ah, 0                   ; Fix overflow (unlikely!)
  1054.         add     bp, 256                 ; Fix address for next block
  1055.  
  1056.         add     al, dh                  ; # of lines
  1057.         mov     dh, 0
  1058.         adc     ah, 0                   ; + carry
  1059.  
  1060.         add     cx, dx                  ; # of words (max 128!)
  1061.  
  1062.         xor     dx, dx                  ; Reset for next block
  1063.  
  1064.         dec     si                      ; More loops?
  1065.  
  1066. JumpCode label byte                     ; This code will be patched to a
  1067.          jz     @@done                  ; JNZ near LoopTop on a 386+ CPU,
  1068.          jmp    LoopTop                 ; saving one instruction in the loop
  1069. @@done:
  1070.  
  1071. IFDEF   ZTIMER
  1072.         call    ZTimerOff
  1073.         call    ZTimerReport
  1074. ENDIF
  1075.  
  1076.     add     [DGROUP:endprog.w_cnt],cx
  1077.     adc     [DGROUP:endprog.w_cnt+2],dx ; == 0
  1078.  
  1079.     add     [DGROUP:endprog.l_cnt],ax
  1080.     adc     [DGROUP:endprog.l_cnt+2],dx ; == 0
  1081.  
  1082.         pop     di
  1083.         pop     dx
  1084.     pop     bx
  1085.  
  1086.     ret
  1087. count   endp
  1088.  
  1089. code    ends
  1090.     end     main
  1091.  
  1092.  
  1093.